using System;
using System.IO;
using System.Drawing;
using System.Collections;
using System.Collections.Specialized;
using System.Text;
using System.Text.RegularExpressions;
using Interop.PrepTools;
using Dimok;
using Dimok.Documents.DataSets;

////////////////////////////////////////////////////////////////////////////////////////////////////
// namespace: Dimok.Documents
//
// summary:	.
////////////////////////////////////////////////////////////////////////////////////////////////////

namespace Dimok.Documents {
	#region Ranges

	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>	Range x coordinate. </summary>
	///
	/// <remarks>	Dimon, 12/21/2010. </remarks>
	////////////////////////////////////////////////////////////////////////////////////////////////////

	public class RangeX	{
		public int Left;
		public int Right;

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Default constructor. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public RangeX(){Init();}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Constructor. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="range">	The range. </param>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public RangeX(string range){
			if( range == null || range == "" )Init();
			else Init(int.Parse(range.Split(',')[0]),int.Parse(range.Split(',')[range.Split(',').Length-1]));
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Constructor. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="range">	The range. </param>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public RangeX(int range):this(range,range){}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Constructor. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="left">		The left. </param>
		/// <param name="right">	The right. </param>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public RangeX(int left,int right){Init(left,right);}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Initialises this object. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		private void Init(){Init(15,30);}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Initialises this object. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="left">		The left. </param>
		/// <param name="right">	The right. </param>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		private void Init(int left,int right){
			Left = left;
			Right = right;
		}
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>	Range y coordinate. </summary>
	///
	/// <remarks>	Dimon, 12/21/2010. </remarks>
	////////////////////////////////////////////////////////////////////////////////////////////////////

	public class RangeY{
		public int Up;
		public int Down;

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Default constructor. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public RangeY(){Init();}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Constructor. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="range">	The range. </param>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public RangeY(string range){
			if( range == null && range == "" )Init();
			else Init(int.Parse(range.Split(',')[0]),int.Parse(range.Split(',')[range.Split(',').Length-1]));
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Constructor. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="range">	The range. </param>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public RangeY(int range):this(range,range){}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Constructor. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="up">		The up. </param>
		/// <param name="down">	The down. </param>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public RangeY(int up,int down){Init(up,down);}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Initialises this object. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		private void Init(){Init(5,5);}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Initialises this object. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="up">		The up. </param>
		/// <param name="down">	The down. </param>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		private void Init(int up,int down){
			Up = up;
			Down = down;
		}
	}
	#endregion Ranges
	
	#region  Token class ===========

	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>	Token. </summary>
	///
	/// <remarks>	Dimon, 12/21/2010. </remarks>
	////////////////////////////////////////////////////////////////////////////////////////////////////

	public class Token {
		public string Text;
		public int X;
		public int Y;

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Constructor. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="text">	The text. </param>
		/// <param name="x">		The x coordinate. </param>
		/// <param name="y">		The y coordinate. </param>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public Token(string text,int x,int y){
			Text = text;
			X = x;
			Y = y;
		}
	}
	#endregion  Token class ===========

	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>	Exception for signalling below range errors. </summary>
	///
	/// <remarks>	Dimon, 12/21/2010. </remarks>
	////////////////////////////////////////////////////////////////////////////////////////////////////

	public class BelowRangeException:ApplicationException{public BelowRangeException():base(){}}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>	Matrix. </summary>
	///
	/// <remarks>	Dimon, 12/21/2010. </remarks>
	////////////////////////////////////////////////////////////////////////////////////////////////////

	public class Matrix {

		#region TextBox Class

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	List of text boxes. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public class TextBoxList : ArrayList{
			public TextBoxList():base(){}
			public static implicit operator Matrix.TextBox(TextBoxList a){return a.Count > 0 ? (Matrix.TextBox)a[0]: null;}
			public static implicit operator Matrix.TextBox[](TextBoxList a){return (Matrix.TextBox[])a.ToArray(typeof(Matrix.TextBox));}
			public static implicit operator string(TextBoxList a){
				return  new StringList(a.ToArray(typeof(string)));
			}
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Text box. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public class TextBox{
			const string NO_TEXT = "";
			public string Text = NO_TEXT;
			public int PageNumber;
			public int PageHeight;
			public int AnchorEdge;
			public int AnchorEdgeFromTop{get{return PageHeight-AnchorEdge;}}
			public bool IsEmpty{get{return Text == NO_TEXT;}}
			public int AnchorLeft;
			public TextBox(int pageNumber,int pageHeigth):this(NO_TEXT,pageNumber,pageHeigth,-1,-1){}
			public TextBox(string text,int pageNumber,int pageHeigth,int anchorEdge,int anchorLeft){
				Text				= text;
				PageNumber	= pageNumber;
				PageHeight	= pageHeigth;
				AnchorEdge	= anchorEdge;
				AnchorLeft = anchorLeft;
			}
			public override string ToString(){return ToString(Environment.NewLine);}
			public string ToString(string separator){
				return "Text:["+Text+"]"+separator+
							 "PageNumber:"+PageNumber+separator+
							 "PageHeight:"+PageHeight+separator+
							 "AnchorBottom:"+AnchorEdge+separator+
							 "AnchorEdgeFromTop:"+AnchorEdgeFromTop+separator+
							 "AnchorLeft:"+AnchorLeft;
			}
			public static implicit operator string(TextBox tb){return tb.ToString();}
		}
		#endregion TextBox Class
		
		#region Static Members

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Find exact phrase. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="fileName">			Filename of the file. </param>
		/// <param name="pattern">			The pattern. </param>
		/// <param name="linePattern">	The line pattern. </param>
		/// <param name="height">				The height. </param>
		/// <param name="width">				The width. </param>
		/// <param name="vStep">				The v step. </param>
		/// <param name="hStep">				Amount to increment by. </param>
		/// <param name="pageNumbers">	A variable-length parameters list containing page numbers. </param>
		///
		/// <returns>	The found phrase. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public static TextBoxList FindPhrase(string fileName,string pattern,string linePattern,int height,int width,int vStep,int hStep,params int[] pageNumbers){
			return PDF.FindText(fileName,pattern,linePattern,height,width,vStep,hStep,pageNumbers);
		}
		#endregion Static Members

		#region Instance Members
		private ArrayList Tokens = new ArrayList();
		private StringBuilder glossary = new StringBuilder();
		private int index = 0;
		public int MaxY = 0;

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Constructor. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="pdfFile">	The pdf file. </param>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public Matrix(byte[] pdfFile){
			IDocClass doc = new IDocClass();
			doc.OpenMem((byte[])pdfFile,"");
			FillToken(doc);
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Constructor. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="fileName">	Filename of the file. </param>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public Matrix(string fileName) {
			IDocClass doc = new IDocClass();
			doc.Open(fileName,"");
			FillToken(doc);
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Gets the glossary. </summary>
		///
		/// <value>	The glossary. </value>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public string Glossary{
			get{ return glossary.ToString(); }
		}

		#region Management

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Fill token. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="doc">	The document. </param>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		private void FillToken(IDocClass doc) {
			int i = 0;
			for( TToken token = doc.GetToken(); token != null; token = doc.GetToken() ){
				string text = token.Text.Trim();
				if( i++ < 50 )MaxY = Math.Max(MaxY,(int)token.Y);
				Add(new Token(text,(int)token.X,(int)token.Y));
				glossary.Append(text);
			}
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Adds token.</summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="token">	The Token to add. </param>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		private void Add(Token token) {
			Tokens.Add(token);
		}
		#endregion Management

		#region Navigation

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Gets the next. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <returns>	. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		private Token Next() {
			if( index < Tokens.Count )return (Token)Tokens[index++];
			index = 0;
			return null;
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Gets the previous. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <returns>	. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		private Token Prev() {
			if( index >= 0 )return (Token)Tokens[index--];
			index = Tokens.Count-1;
			return null;
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Gets the current. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <returns>	. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		private Token Current() {
			return index<0 || index == Tokens.Count ? null : (Token)Tokens[index];
		}
		#endregion Navigation
		
		#region FindPhrase

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Find exact phrase. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="phrases">	The phrases. </param>
		///
		/// <returns>	true if it succeeds, false if it fails. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public bool FindPhrase(ICollection phrases) {
			foreach(string phrase in phrases)
				if(FindPhrase(phrase,true) != null) return true;
			return false;
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Find exact phrase. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="phrase">	. </param>
		///
		/// <returns>	The found phrase. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public Token FindPhrase(string phrase) {return FindPhrase(phrase,false);}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Find exact phrase. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="phrase">	. </param>
		/// <param name="rewind">	. </param>
		///
		/// <returns>	The found phrase. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public Token FindPhrase(string phrase,bool rewind){return FindPhrase(phrase,10000,0,rewind);}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Find exact phrase. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="phrase">	. </param>
		/// <param name="yUp">		. </param>
		/// <param name="yDown">	. </param>
		///
		/// <returns>	The found phrase. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public Token FindPhrase(string phrase,int yUp,int yDown) {return FindPhrase(phrase,yUp,yDown,false);}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Find exact phrase. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="phrase">	. </param>
		/// <param name="yUp">		. </param>
		/// <param name="yDown">	. </param>
		/// <param name="rewind">	. </param>
		///
		/// <returns>	The found phrase. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public Token FindPhrase(string phrase,int yUp,int yDown,bool rewind) {
			if( rewind )this.index = 0;
			int index = this.index;
			foreach( string ph in phrase.Split('|') ) {
				Token t = FindPhrase(ph.Split(' '),yUp,yDown,rewind);
				if( t != null )return t;
				this.index = index;
			}
			return null;
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Find exact phrase. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="phrase">	. </param>
		/// <param name="rewind">	. </param>
		///
		/// <returns>	The found phrase. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		private Token FindPhrase(string[] phrase,bool rewind){return FindPhrase(phrase,10000,0,rewind);}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Find exact phrase. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="phrase">	. </param>
		/// <param name="yUp">		. </param>
		/// <param name="yDown">	. </param>
		/// <param name="rewind">	. </param>
		///
		/// <returns>	The found phrase. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		private Token FindPhrase(string[] phrase,int yUp,int yDown,bool rewind) {
			try {
				if( rewind )index = 0;
				Token next = FindToken(phrase[0],yUp,yDown);
				if( next == null )return null;
				for( int i=1 ; i < phrase.Length; i++ )
					if( FindToken(phrase[i],1) == null )
						return FindPhrase(phrase,yUp,yDown,false);// Go find next
				return Current()==null ? null : next;
			}
			catch( BelowRangeException ) {
				return null;
			}
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Finds ANY phrase contained in StringCollection. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="phrases">	The phrases. </param>
		///
		/// <returns>	true if it succeeds, false if it fails. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public bool FindAllTokens(ICollection phrases)	{return FindAllTokens(phrases,1);}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Finds ANY phrase contained in StringCollection. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="phrases">		The phrases. </param>
		/// <param name="matchCount">	Number of matches. </param>
		///
		/// <returns>	true if it succeeds, false if it fails. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public bool FindAllTokens(ICollection phrases,int matchCount) {
			foreach( string phrase in phrases )
				if( FindAllTokens(phrase) && --matchCount == 0 )return true;
			return false;
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Finds ANY phrase contained in StringCollection. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="phrase">	words. </param>
		///
		/// <returns>	true if it succeeds, false if it fails. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public bool FindAllTokens(string phrase) {
			return FindAllTokens(phrase.Split(' '));
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Search for words in any order. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="phrase">	words. </param>
		///
		/// <returns>	true - if all words are found. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public bool FindAllTokens(string[] phrase) {
			for( int i=0 ; i < phrase.Length; i++ ) {
				index = 0;
				if( FindToken(phrase[i]) == null )
					return false;
			}
			return true;
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Searches for the first token. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="token">	The Token to add. </param>
		///
		/// <returns>	The found token. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		private Token FindToken(string token) {return FindToken(token,int.MaxValue);}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Searches for the first token. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="token">	The Token to add. </param>
		/// <param name="steps">	The steps. </param>
		///
		/// <returns>	The found token. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		private Token FindToken(string token,int steps) {return FindToken(token,steps,10000,0);}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Searches for the first token. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="token">	The Token to add. </param>
		/// <param name="yUp">		. </param>
		/// <param name="yDown">	. </param>
		///
		/// <returns>	The found token. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		private Token FindToken(string token,int yUp,int yDown) {return FindToken(token,int.MaxValue,yUp,yDown);}

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Searches for the first token. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="token">	The Token to add. </param>
		/// <param name="steps">	The steps. </param>
		/// <param name="yUp">		. </param>
		/// <param name="yDown">	. </param>
		///
		/// <returns>	The found token. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		private Token FindToken(string token,int steps,int yUp,int yDown) {
			while( Current() != null && steps-- > 0 )
				try{
					if( string.Compare(token,Current().Text,true) == 0 && Current().Y<=yUp && Current().Y >= yDown )return Current();
					//if( Current() != null && Current().Y > yDown )throw new BelowRangeException();
				}finally{ Next(); }
			return null;
		}
		#endregion FindPhrase

		////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>	Searches for the first location. </summary>
		///
		/// <remarks>	Dimon, 12/21/2010. </remarks>
		///
		/// <param name="x">	The x coordinate. </param>
		/// <param name="y">	The y coordinate. </param>
		/// <param name="X">	The x coordinate. </param>
		/// <param name="Y">	The y coordinate. </param>
		///
		/// <returns>	The found location. </returns>
		////////////////////////////////////////////////////////////////////////////////////////////////////

		public Token FindLocation(int x,int y,RangeX X,RangeY Y) {
			index = 0;
			for( Token token = Next(); token != null /*&& token.Y > y-Y.Down*/; token = Next() )
				if( token.X > x-X.Left && token.X < x+X.Right && token.Y > y-Y.Down && token.Y < y+Y.Up )
					return token;
			return null;
		}
		#endregion
	}
}